﻿/*******************************************************************************
 * AAEngine
 * Copyright (c) 2010 Mike Jarosch
 *
 * Original source PushButton Engine:
 * Copyright (C) 2009 PushButton Labs, LLC
 * For more information see http://www.pushbuttonengine.com
 *
 * This file is licensed under the terms of the MIT license, which is included
 * in the "MIT License.txt" file at the root directory of this SDK.
 ******************************************************************************/

using System.Collections.Generic;
using AAEngine.Engine.Debug;

namespace AAEngine.Engine.Core
{
    /// <summary>
    /// The ObjectTypeManager, together with the ObjectType class, is essentially an abstraction
    /// of a bitmask to allow objects to be identified with friendly names, rather than complicated
    /// numbers.
    /// </summary>
    public class ObjectTypeManager
    {
        private int _typeCount = 0;
        private Dictionary<string, int> _typeList = new Dictionary<string, int>();
//        private List<string> _bitList = new List<string>();
        private Dictionary<int, string> _bitList = new Dictionary<int, string>();

        /// <summary>
        /// The number of object types that have been registered.
        /// </summary>
        public int TypeCount
        {
            get { return _typeCount; }
        }

        public int GetType(string typeName)
        {
            if (!_typeList.ContainsKey(typeName))
            {
                if (_typeCount == 32)
                {
                    Logger.Warn(this, "GetType", "Only 64 unique object types can be created.");
                    return 0;
                }

                _typeList[typeName] = _typeCount;
                _bitList[1 << _typeCount] = typeName;
                _typeCount++;
            }

            return 1 << _typeList[typeName];
        }

        /// <summary>
        /// Gets the name of an object type based on the number it was assigned.
        /// </summary>
        /// <param name="number">The number of the type to find.</param>
        /// <returns>
        /// The name of the type with the specified number, or null if 
        /// the number is not assigned to any type.
        /// </returns>
        public string GetTypeName(int number)
        {
            if (!_bitList.ContainsKey(number))
            {
                return null;
            }
            return _bitList[number];
        }

        /// <summary>
        /// Determines whether an object type is of the specified type.
        /// </summary>
        /// <param name="type">The type to check.</param>
        /// <param name="typeName">The name of the type to check.</param>
        /// <returns>
        /// True if the specified type is of the specified type name. Keep in
        /// mind, the type must match exactly, meaning, if it has multiple type names
        /// associated with it, this will always return false.
        /// </returns>
        public bool DoesTypeMatch(ObjectType type, string typeName)
        {
            if (_typeList.ContainsKey(typeName))
            {
                int t = _typeList[typeName];
                return type.Bits == 1 << t;
            }
            return false;
        }

        /// <summary>
        /// Determines whether an object type contains the specified type.
        /// </summary>
        /// <param name="type">The type to check.</param>
        /// <param name="typeName">The name of the type to check.</param>
        /// <returns>
        /// True if the specified type is of the specified type name. Keep in
        /// mind, the type must only contain the type name, meaning, if it has multiple
        /// type names associated with it, only one of them has to match.
        /// </returns>
        public bool DoesTypeOverlap(ObjectType type, string typeName)
        {
            if (_typeList.ContainsKey(typeName))
            {
                int t = _typeList[typeName];
                return (type.Bits & (1 << t)) != 0;
            }
            return false;
        }

        /// <summary>
        /// Determines whether two object types are of the same type.
        /// </summary>
        /// <param name="type1">The type to check.</param>
        /// <param name="type2">The type to check against.</param>
        /// <returns>True if type1 and type2 contain the exact same types.</returns>
        public bool DoTypesMatch(ObjectType type1, ObjectType type2)
        {
            return type1.Bits == type2.Bits;
        }

        /// <summary>
        /// Determines whether two object types have overlapping types.
        /// </summary>
        /// <param name="type1">The type to check.</param>
        /// <param name="type2">The type to check against.</param>
        /// <returns>True if type1 has any of the type contained in type2.</returns>
        public bool DoTypesOverlap(ObjectType type1, ObjectType type2)
        {
            if (type1 == null || type2 == null)
            {
                return false;
            }

            return (type1.Bits & type2.Bits) != 0;
        }

        public void RegisterFlag(int bitIndex, string name)
        {
            // Sanity
            if (GetTypeName(bitIndex) != null)
            {
                throw new AAException("Bit already in use!");
            }
            if (_typeList.ContainsKey(name))
            {
                throw new AAException("Name already assigned to another bit!");
            }

            // Update typeCount so subsequent updates still work. This may
            // cause wasted bits
            if (bitIndex >= _typeCount)
            {
                _typeCount = bitIndex + 1;
            }

            // And stuff into our arrays
            _typeList[name] = bitIndex;
            _bitList[bitIndex] = name;
        }
    }
}
